//! Protocol Buffers Serialization Example
//!
//! This example demonstrates how to use Protocol Buffers for serialization and compares it with MessagePack.
//!
//! Run with:
//! ```bash
//! cargo run --example remote_protobuf --features "remote protobuf"
//! ```

#[cfg(all(feature = "remote", feature = "protobuf"))]
use kameo::{
    serialization::{self, SerializationConfig, SerializationFormat},
};
#[cfg(all(feature = "remote", feature = "protobuf"))]
use prost::Message as ProstMessage;
#[cfg(all(feature = "remote", feature = "protobuf"))]
use serde::{Deserialize, Serialize};

#[cfg(all(feature = "remote", feature = "protobuf"))]
// Define Protocol Buffer message for increment
#[derive(Clone, ProstMessage)]
pub struct IncrementProto {
    #[prost(uint32, tag = "1")]
    pub amount: u32,
}

#[cfg(all(feature = "remote", feature = "protobuf"))]
// Regular message for serialization
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Increment {
    pub amount: u32,
}

#[cfg(all(feature = "remote", feature = "protobuf"))]
// Implementation of conversion between protobuf and regular messages
impl From<IncrementProto> for Increment {
    fn from(proto: IncrementProto) -> Self {
        Self {
            amount: proto.amount,
        }
    }
}

#[cfg(all(feature = "remote", feature = "protobuf"))]
impl From<Increment> for IncrementProto {
    fn from(inc: Increment) -> Self {
        Self {
            amount: inc.amount,
        }
    }
}

#[cfg(all(feature = "remote", feature = "protobuf"))]
// Define Protocol Buffer message for decrement
#[derive(Clone, ProstMessage)]
pub struct DecrementProto {
    #[prost(uint32, tag = "1")]
    pub amount: u32,
}

#[cfg(all(feature = "remote", feature = "protobuf"))]
// Regular message for serialization
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Decrement {
    pub amount: u32,
}

#[cfg(all(feature = "remote", feature = "protobuf"))]
// Implementation of conversion between protobuf and regular messages
impl From<DecrementProto> for Decrement {
    fn from(proto: DecrementProto) -> Self {
        Self {
            amount: proto.amount,
        }
    }
}

#[cfg(all(feature = "remote", feature = "protobuf"))]
impl From<Decrement> for DecrementProto {
    fn from(dec: Decrement) -> Self {
        Self {
            amount: dec.amount,
        }
    }
}

#[cfg(all(feature = "remote", feature = "protobuf"))]
// Example that demonstrates Protocol Buffers serialization
fn demonstrate_protobuf_serialization() {
    // Create a Protocol Buffer message
    let increment_proto = IncrementProto {
        amount: 5,
    };
    
    // Serialize to Protocol Buffers format
    let proto_bytes = increment_proto.encode_to_vec();
    println!("Protocol Buffer serialized size: {} bytes", proto_bytes.len());
    println!("Hex representation: {}", hex_string(&proto_bytes));
    
    // Deserialize back to Protocol Buffer message
    let decoded_proto = IncrementProto::decode(proto_bytes.as_slice()).unwrap();
    println!("Decoded Protocol Buffer: amount={}", decoded_proto.amount);
    
    // Convert to actor message
    let increment_msg: Increment = decoded_proto.into();
    println!("Converted to actor message: amount={}", increment_msg.amount);
    
    // Also demonstrate MessagePack serialization for comparison
    let serialize_config = SerializationConfig::new().with_format(SerializationFormat::MessagePack);
    serialization::init_serialization(serialize_config);
    
    // Serialize with MessagePack
    let msgpack_bytes = rmp_serde::to_vec(&increment_msg).unwrap();
    println!("\nMessagePack serialized size: {} bytes", msgpack_bytes.len());
    println!("Hex representation: {}", hex_string(&msgpack_bytes));
    
    // Calculate size ratio
    let size_ratio = (proto_bytes.len() as f64) / (msgpack_bytes.len() as f64);
    println!("\nProtocolBuffers vs MessagePack size ratio: {:.2}x", size_ratio);
    
    // Also demonstrate with Decrement message
    let decrement_proto = DecrementProto {
        amount: 3,
    };
    
    let proto_bytes = decrement_proto.encode_to_vec();
    println!("\nDecrement - Protocol Buffer serialized size: {} bytes", proto_bytes.len());
    
    let decrement_msg: Decrement = decrement_proto.into();
    let msgpack_bytes = rmp_serde::to_vec(&decrement_msg).unwrap();
    println!("Decrement - MessagePack serialized size: {} bytes", msgpack_bytes.len());
    
    println!("\nConclusion:");
    println!("Protocol Buffers provides efficient binary serialization with schema validation.");
    println!("It's particularly well-suited for cross-language communication and versioning.");
    println!("For simple messages, Protocol Buffers and MessagePack have similar efficiency.");
    println!("As message complexity increases, Protocol Buffers' schema-based approach can provide better size optimization.");
}

#[cfg(all(feature = "remote", feature = "protobuf"))]
// Helper function to display hex representation
fn hex_string(bytes: &[u8]) -> String {
    bytes.iter()
        .map(|b| format!("{:02x}", b))
        .collect::<Vec<_>>()
        .join("")
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    #[cfg(all(feature = "remote", feature = "protobuf"))]
    {
        // Demonstrate Protocol Buffers serialization
        demonstrate_protobuf_serialization();
    }
    
    #[cfg(not(all(feature = "remote", feature = "protobuf")))]
    {
        println!("This example requires both 'remote' and 'protobuf' features. Run with:\ncargo run --example remote_protobuf --features \"remote protobuf\"");
    }
    
    Ok(())
} 